// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_JSON_JSON_VALUE_CONVERTER_H_
#define BASE_JSON_JSON_VALUE_CONVERTER_H_

#include <stddef.h>

#include <memory>
#include <string>
#include <vector>

#include "base/base_export.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "base/values.h"

// JSONValueConverter converts a JSON value into a C++ struct in a
// lightweight way.
//
// Usage:
// For real examples, you may want to refer to _unittest.cc file.
//
// Assume that you have a struct like this:
//   struct Message {
//     int foo;
//     std::string bar;
//     static void RegisterJSONConverter(
//         JSONValueConverter<Message>* converter);
//   };
//
// And you want to parse a json data into this struct.  First, you
// need to declare RegisterJSONConverter() method in your struct.
//   // static
//   void Message::RegisterJSONConverter(
//       JSONValueConverter<Message>* converter) {
//     converter->RegisterIntField("foo", &Message::foo);
//     converter->RegisterStringField("bar", &Message::bar);
//   }
//
// Then, you just instantiate your JSONValueConverter of your type and call
// Convert() method.
//   Message message;
//   JSONValueConverter<Message> converter;
//   converter.Convert(json, &message);
//
// Convert() returns false when it fails.  Here "fail" means that the value is
// structurally different from expected, such like a string value appears
// for an int field.  Do not report failures for missing fields.
// Also note that Convert() will modify the passed |message| even when it
// fails for performance reason.
//
// For nested field, the internal message also has to implement the registration
// method.  Then, just use RegisterNestedField() from the containing struct's
// RegisterJSONConverter method.
//   struct Nested {
//     Message foo;
//     static void RegisterJSONConverter(...) {
//       ...
//       converter->RegisterNestedField("foo", &Nested::foo);
//     }
//   };
//
// For repeated field, we just assume std::vector<std::unique_ptr<ElementType>>
// for its container and you can put RegisterRepeatedInt or some other types.
// Use RegisterRepeatedMessage for nested repeated fields.
//
// Sometimes JSON format uses string representations for other types such
// like enum, timestamp, or URL.  You can use RegisterCustomField method
// and specify a function to convert a StringPiece to your type.
//   bool ConvertFunc(const StringPiece& s, YourEnum* result) {
//     // do something and return true if succeed...
//   }
//   struct Message {
//     YourEnum ye;
//     ...
//     static void RegisterJSONConverter(...) {
//       ...
//       converter->RegsiterCustomField<YourEnum>(
//           "your_enum", &Message::ye, &ConvertFunc);
//     }
//   };

namespace base {

template <typename StructType>
class JSONValueConverter;

namespace internal {

    template <typename StructType>
    class FieldConverterBase {
    public:
        explicit FieldConverterBase(const std::string& path)
            : field_path_(path)
        {
        }
        virtual ~FieldConverterBase() { }
        virtual bool ConvertField(const base::Value& value, StructType* obj)
            const = 0;
        const std::string& field_path() const { return field_path_; }

    private:
        std::string field_path_;
        DISALLOW_COPY_AND_ASSIGN(FieldConverterBase);
    };

    template <typename FieldType>
    class ValueConverter {
    public:
        virtual ~ValueConverter() { }
        virtual bool Convert(const base::Value& value, FieldType* field) const = 0;
    };

    template <typename StructType, typename FieldType>
    class FieldConverter : public FieldConverterBase<StructType> {
    public:
        explicit FieldConverter(const std::string& path,
            FieldType StructType::*field,
            ValueConverter<FieldType>* converter)
            : FieldConverterBase<StructType>(path)
            , field_pointer_(field)
            , value_converter_(converter)
        {
        }

        bool ConvertField(const base::Value& value, StructType* dst) const override
        {
            return value_converter_->Convert(value, &(dst->*field_pointer_));
        }

    private:
        FieldType StructType::*field_pointer_;
        std::unique_ptr<ValueConverter<FieldType>> value_converter_;
        DISALLOW_COPY_AND_ASSIGN(FieldConverter);
    };

    template <typename FieldType>
    class BasicValueConverter;

    template <>
    class BASE_EXPORT BasicValueConverter<int> : public ValueConverter<int> {
    public:
        BasicValueConverter() { }

        bool Convert(const base::Value& value, int* field) const override;

    private:
        DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
    };

    template <>
    class BASE_EXPORT BasicValueConverter<std::string>
        : public ValueConverter<std::string> {
    public:
        BasicValueConverter() { }

        bool Convert(const base::Value& value, std::string* field) const override;

    private:
        DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
    };

    template <>
    class BASE_EXPORT BasicValueConverter<string16>
        : public ValueConverter<string16> {
    public:
        BasicValueConverter() { }

        bool Convert(const base::Value& value, string16* field) const override;

    private:
        DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
    };

    template <>
    class BASE_EXPORT BasicValueConverter<double> : public ValueConverter<double> {
    public:
        BasicValueConverter() { }

        bool Convert(const base::Value& value, double* field) const override;

    private:
        DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
    };

    template <>
    class BASE_EXPORT BasicValueConverter<bool> : public ValueConverter<bool> {
    public:
        BasicValueConverter() { }

        bool Convert(const base::Value& value, bool* field) const override;

    private:
        DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
    };

    template <typename FieldType>
    class ValueFieldConverter : public ValueConverter<FieldType> {
    public:
        typedef bool (*ConvertFunc)(const base::Value* value, FieldType* field);

        explicit ValueFieldConverter(ConvertFunc convert_func)
            : convert_func_(convert_func)
        {
        }

        bool Convert(const base::Value& value, FieldType* field) const override
        {
            return convert_func_(&value, field);
        }

    private:
        ConvertFunc convert_func_;

        DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter);
    };

    template <typename FieldType>
    class CustomFieldConverter : public ValueConverter<FieldType> {
    public:
        typedef bool (*ConvertFunc)(const StringPiece& value, FieldType* field);

        explicit CustomFieldConverter(ConvertFunc convert_func)
            : convert_func_(convert_func)
        {
        }

        bool Convert(const base::Value& value, FieldType* field) const override
        {
            std::string string_value;
            return value.GetAsString(&string_value) && convert_func_(string_value, field);
        }

    private:
        ConvertFunc convert_func_;

        DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter);
    };

    template <typename NestedType>
    class NestedValueConverter : public ValueConverter<NestedType> {
    public:
        NestedValueConverter() { }

        bool Convert(const base::Value& value, NestedType* field) const override
        {
            return converter_.Convert(value, field);
        }

    private:
        JSONValueConverter<NestedType> converter_;
        DISALLOW_COPY_AND_ASSIGN(NestedValueConverter);
    };

    template <typename Element>
    class RepeatedValueConverter
        : public ValueConverter<std::vector<std::unique_ptr<Element>>> {
    public:
        RepeatedValueConverter() { }

        bool Convert(const base::Value& value,
            std::vector<std::unique_ptr<Element>>* field) const override
        {
            const base::ListValue* list = NULL;
            if (!value.GetAsList(&list)) {
                // The field is not a list.
                return false;
            }

            field->reserve(list->GetSize());
            for (size_t i = 0; i < list->GetSize(); ++i) {
                const base::Value* element = NULL;
                if (!list->Get(i, &element))
                    continue;

                std::unique_ptr<Element> e(new Element);
                if (basic_converter_.Convert(*element, e.get())) {
                    field->push_back(std::move(e));
                } else {
                    DVLOG(1) << "failure at " << i << "-th element";
                    return false;
                }
            }
            return true;
        }

    private:
        BasicValueConverter<Element> basic_converter_;
        DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter);
    };

    template <typename NestedType>
    class RepeatedMessageConverter
        : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
    public:
        RepeatedMessageConverter() { }

        bool Convert(const base::Value& value,
            std::vector<std::unique_ptr<NestedType>>* field) const override
        {
            const base::ListValue* list = NULL;
            if (!value.GetAsList(&list))
                return false;

            field->reserve(list->GetSize());
            for (size_t i = 0; i < list->GetSize(); ++i) {
                const base::Value* element = NULL;
                if (!list->Get(i, &element))
                    continue;

                std::unique_ptr<NestedType> nested(new NestedType);
                if (converter_.Convert(*element, nested.get())) {
                    field->push_back(std::move(nested));
                } else {
                    DVLOG(1) << "failure at " << i << "-th element";
                    return false;
                }
            }
            return true;
        }

    private:
        JSONValueConverter<NestedType> converter_;
        DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter);
    };

    template <typename NestedType>
    class RepeatedCustomValueConverter
        : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
    public:
        typedef bool (*ConvertFunc)(const base::Value* value, NestedType* field);

        explicit RepeatedCustomValueConverter(ConvertFunc convert_func)
            : convert_func_(convert_func)
        {
        }

        bool Convert(const base::Value& value,
            std::vector<std::unique_ptr<NestedType>>* field) const override
        {
            const base::ListValue* list = NULL;
            if (!value.GetAsList(&list))
                return false;

            field->reserve(list->GetSize());
            for (size_t i = 0; i < list->GetSize(); ++i) {
                const base::Value* element = NULL;
                if (!list->Get(i, &element))
                    continue;

                std::unique_ptr<NestedType> nested(new NestedType);
                if ((*convert_func_)(element, nested.get())) {
                    field->push_back(std::move(nested));
                } else {
                    DVLOG(1) << "failure at " << i << "-th element";
                    return false;
                }
            }
            return true;
        }

    private:
        ConvertFunc convert_func_;
        DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter);
    };

} // namespace internal

template <class StructType>
class JSONValueConverter {
public:
    JSONValueConverter()
    {
        StructType::RegisterJSONConverter(this);
    }

    void RegisterIntField(const std::string& field_name,
        int StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<StructType, int>>(
            field_name, field, new internal::BasicValueConverter<int>));
    }

    void RegisterStringField(const std::string& field_name,
        std::string StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType, std::string>>(
                field_name, field, new internal::BasicValueConverter<std::string>));
    }

    void RegisterStringField(const std::string& field_name,
        string16 StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType, string16>>(
                field_name, field, new internal::BasicValueConverter<string16>));
    }

    void RegisterBoolField(const std::string& field_name,
        bool StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<StructType, bool>>(
            field_name, field, new internal::BasicValueConverter<bool>));
    }

    void RegisterDoubleField(const std::string& field_name,
        double StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<StructType, double>>(
            field_name, field, new internal::BasicValueConverter<double>));
    }

    template <class NestedType>
    void RegisterNestedField(
        const std::string& field_name, NestedType StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType, NestedType>>(
                field_name, field, new internal::NestedValueConverter<NestedType>));
    }

    template <typename FieldType>
    void RegisterCustomField(
        const std::string& field_name,
        FieldType StructType::*field,
        bool (*convert_func)(const StringPiece&, FieldType*))
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType, FieldType>>(
                field_name, field,
                new internal::CustomFieldConverter<FieldType>(convert_func)));
    }

    template <typename FieldType>
    void RegisterCustomValueField(
        const std::string& field_name,
        FieldType StructType::*field,
        bool (*convert_func)(const base::Value*, FieldType*))
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType, FieldType>>(
                field_name, field,
                new internal::ValueFieldConverter<FieldType>(convert_func)));
    }

    void RegisterRepeatedInt(
        const std::string& field_name,
        std::vector<std::unique_ptr<int>> StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<StructType,
                std::vector<std::unique_ptr<int>>>>(
                field_name, field, new internal::RepeatedValueConverter<int>));
    }

    void RegisterRepeatedString(
        const std::string& field_name,
        std::vector<std::unique_ptr<std::string>> StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<std::string>>>>(
                field_name, field,
                new internal::RepeatedValueConverter<std::string>));
    }

    void RegisterRepeatedString(
        const std::string& field_name,
        std::vector<std::unique_ptr<string16>> StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<string16>>>>(
            field_name, field, new internal::RepeatedValueConverter<string16>));
    }

    void RegisterRepeatedDouble(
        const std::string& field_name,
        std::vector<std::unique_ptr<double>> StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<double>>>>(
            field_name, field, new internal::RepeatedValueConverter<double>));
    }

    void RegisterRepeatedBool(
        const std::string& field_name,
        std::vector<std::unique_ptr<bool>> StructType::*field)
    {
        fields_.push_back(MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<bool>>>>(
            field_name, field, new internal::RepeatedValueConverter<bool>));
    }

    template <class NestedType>
    void RegisterRepeatedCustomValue(
        const std::string& field_name,
        std::vector<std::unique_ptr<NestedType>> StructType::*field,
        bool (*convert_func)(const base::Value*, NestedType*))
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<NestedType>>>>(
                field_name, field,
                new internal::RepeatedCustomValueConverter<NestedType>(
                    convert_func)));
    }

    template <class NestedType>
    void RegisterRepeatedMessage(
        const std::string& field_name,
        std::vector<std::unique_ptr<NestedType>> StructType::*field)
    {
        fields_.push_back(
            MakeUnique<internal::FieldConverter<
                StructType, std::vector<std::unique_ptr<NestedType>>>>(
                field_name, field,
                new internal::RepeatedMessageConverter<NestedType>));
    }

    bool Convert(const base::Value& value, StructType* output) const
    {
        const DictionaryValue* dictionary_value = NULL;
        if (!value.GetAsDictionary(&dictionary_value))
            return false;

        for (size_t i = 0; i < fields_.size(); ++i) {
            const internal::FieldConverterBase<StructType>* field_converter = fields_[i].get();
            const base::Value* field = NULL;
            if (dictionary_value->Get(field_converter->field_path(), &field)) {
                if (!field_converter->ConvertField(*field, output)) {
                    DVLOG(1) << "failure at field " << field_converter->field_path();
                    return false;
                }
            }
        }
        return true;
    }

private:
    std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>>
        fields_;

    DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
};

} // namespace base

#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_
